//
//  YNSLRoster.m
//  Messenger
//
//  Created by iOS on 14/12/24.
//  Copyright (c) 2014年 孙浪. All rights reserved.
//
//  用户名册的数据
//

#import "YNSLRoster.h"
#import "XMPP.h"
#import "XMPPIDTracker.h"
#import "XMPPLogging.h"
#import "XMPPFramework.h"
#import "DDList.h"
#import "YNIMTool.h"
#import "YNContactManager.h"
#import "YNMessagerReceiveHandle.h"
#import "YNMessageContactManager.h"
#import "YNIMFrameworkMacro.h"

#if ! __has_feature(objc_arc)
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
#endif

#define YNXMPP_IQ_TIMEOUT 20
// Log levels: off, error, warn, info, verbose
// Log flags: trace

#if DEBUG
    static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN; // | XMPP_LOG_FLAG_TRACE;
#else
    static const int xmppLogLevel = XMPP_LOG_LEVEL_OFF;
#endif

enum YNSLRosterConfig
{
    kAutoFetchRoster = 1 << 0,                                  //认证成功后，自动获取roster
    kRosterlessOperation = 1 << 1,                              //只获取Roster里面在线好友
};

enum YNSLRosterFlags
{
    kRequestedRoster    = 1 << 0,       //If set, we have requested the roster(已经请求roster)
    kHasRoster          = 1 << 1,       //If set, we have received the roster (已经接收roster)
    kPopulatingRoster   = 1 << 2,       //If set, we are populating the roster(已经填充roster)
};


@interface YNSLRoster ()
//机构编号
@property(nonatomic,copy)NSString*companycode;
//用户imId
@property(nonatomic,copy)NSString*userId;

@property(nonatomic,weak)id<YNXMPPprotocol> delegate;
@end



////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@implementation YNSLRoster
- (instancetype)initWithCode:(NSString*)code userId:(NSString*)userId delegate:(id<YNXMPPprotocol>)delegate
{
    self.companycode = code;
    self.userId = userId;
    self.delegate = delegate;
    return [self initWithRosterDispatchQueue:NULL];
}

- (id)initWithRosterDispatchQueue:(dispatch_queue_t)queue
{
    if ((self = [super initWithDispatchQueue:queue])) {
        
        config = kAutoFetchRoster | kRosterlessOperation;
        flags = 0;
        mucModules = [[DDList alloc] init];
    }
    
    return self;
    
}

- (BOOL)activate:(XMPPStream *)aXmppStream
{
    XMPPLogTrace();
    
    if ([super activate:aXmppStream]) {
        
        XMPPLogVerbose(@"%@: Activated", THIS_FILE);
        
        xmppIDTracker = [[XMPPIDTracker alloc] initWithStream:xmppStream dispatchQueue:moduleQueue];

        return YES;
    }
    
    return NO;
}

- (void)deactivate
{
    XMPPLogTrace();
    
    dispatch_block_t block = ^{ @autoreleasepool {
        
        [xmppIDTracker removeAllIDs];
        xmppIDTracker = nil;
        
    }};
    
    if (dispatch_get_specific(moduleQueueTag)) {
        block();
    }else {
        dispatch_sync(moduleQueue, block);
    }
    
    [super deactivate];
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Configuration
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (BOOL)autoFetchRoster
{
    __block BOOL result = NO;
    
    dispatch_block_t block = ^{
      
        result = (config & kAutoFetchRoster) ? YES : NO;
    };
    
    if (dispatch_get_specific(moduleQueueTag)) {
        block();
    }else {
        dispatch_sync(moduleQueue, block);
    }
    
    return result;
    
}

- (void)setAutoFetchRoster:(BOOL)flag
{
    dispatch_block_t block = ^{
      
        if (flag) {
            
            config |= kAutoFetchRoster;
            
        }else {
            
            config &= ~kAutoFetchRoster;
        }
    };
    if (dispatch_get_specific(moduleQueueTag)) {
        block();
    }else {
        dispatch_sync(moduleQueue, block);
    }
}

- (BOOL)allowRosterlessOperation
{
    __block BOOL result = NO;
    
    dispatch_block_t block = ^{
        
        result = (config & kRosterlessOperation) ? YES : NO;
    };
    
    if (dispatch_get_specific(moduleQueueTag)) {
        block();
    }else {
        dispatch_sync(moduleQueue, block);
    }
    
    return result;
}

- (void)setAllowRosterlessOperation:(BOOL)flag
{
    dispatch_block_t block = ^{
        
        if (flag)
            config |= kRosterlessOperation;
        else
            config &= ~kRosterlessOperation;
    };
    
    if (dispatch_get_specific(moduleQueueTag))
        block();
    else
        dispatch_async(moduleQueue, block);
}

- (BOOL)hasRequestedRoster
{
    __block BOOL result = NO;
    
    dispatch_block_t block = ^{
        result = (flags & kRequestedRoster) ? YES : NO;
    };
    
    if (dispatch_get_specific(moduleQueueTag))
        block();
    else
        dispatch_sync(moduleQueue, block);
    
    return result;
}

- (BOOL)isPopulating{
    
    __block BOOL result = NO;
    
    dispatch_block_t block = ^{
        result = (flags & kPopulatingRoster) ? YES : NO;
    };
    
    if (dispatch_get_specific(moduleQueueTag))
        block();
    else
        dispatch_sync(moduleQueue, block);
    
    return result;
}

- (BOOL)hasRoster
{
    __block BOOL result = NO;
    
    dispatch_block_t block = ^{
        result = (flags & kHasRoster) ? YES : NO;
    };
    
    if (dispatch_get_specific(moduleQueueTag))
        block();
    else
        dispatch_sync(moduleQueue, block);
    
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Utilities
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (BOOL)_requestedRoster
{
    NSAssert(dispatch_get_specific(moduleQueueTag), @"Invoked on incorrect queue");
    
    return (flags & kRequestedRoster) ? YES : NO;
}

- (void)_setRequestedRoster:(BOOL)flag
{
    NSAssert(dispatch_get_specific(moduleQueueTag) , @"Invoked on incorrect queue");
    
    if (flag)
        flags |= kRequestedRoster; //yes
    else
        flags &= ~kRequestedRoster; //no
}

- (void)_setHasRoster:(BOOL)flag
{
    NSAssert(dispatch_get_specific(moduleQueueTag) , @"Invoked on incorrect queue");
    
    if (flag)
        flags |= kHasRoster;
    else
        flags &= ~kHasRoster;
}

- (BOOL)_populatingRoster
{
    NSAssert(dispatch_get_specific(moduleQueueTag) , @"Invoked on incorrect queue");
    
    return (flags & kPopulatingRoster) ? YES : NO;
}

- (void)_setPopulatingRoster:(BOOL)flag
{
    NSAssert(dispatch_get_specific(moduleQueueTag) , @"Invoked on incorrect queue");
    
    if (flag)
        flags |= kPopulatingRoster;
    else
        flags &= ~kPopulatingRoster;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Roster Management
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (void)setNickname:(NSString *)nickname forUser:(XMPPJID *)jid
{
    // This is a public method, so it may be invoked on any thread/queue.

    if (jid == nil) {
        return;
    }

    // <iq type="set">
    //   <query xmlns="jabber:iq:roster">
    //     <item jid="bareJID" name="nickname"/>
    //   </query>
    // </iq>

    NSXMLElement *item = [NSXMLElement elementWithName:@"item"];
    [item addAttributeWithName:@"jid" stringValue:[jid bare]];
    [item addAttributeWithName:@"name" stringValue:nickname];

    NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:roster"];
    [query addChild:item];

//    NSXMLElement *iq = [XMPPIQ iqWithType:@"set"];
    NSXMLElement *iq = [NSXMLElement elementWithName:@"id"];
    [iq addAttributeWithName:@"type" stringValue:@"set"];
    [iq addChild:query];

    [xmppStream sendElement:iq];
}

/**
 *
 * 获取组织机构信息.
 *
 * isIncremUpdate, 数据类型为BOOL, YES :增量更新 NO:全量更新。
 *
 */
- (void)fetchRoster
{
    if (self.contactOrgDataIsSaveing == YES) {
        return;
    }
    self.contactOrgDataIsSaveing=YES;
    // This is a public method, so it may be invoked on any thread/queue.
    dispatch_block_t block = ^{ @autoreleasepool {

        /*请求IQ样式
         <iq id="URRYf-31"
            type="get"
            from="xiongshihui@m.com/Spark 2.6.3"
            to="admin@m.com">
            <req xmlns="com:yineng:orgget">
                <params>
                {"servertime":"1399948037000"}
                /params>
            </req>
         </iq>
        */

        if ([self _requestedRoster]) {
            //已经从服务器请求到Roster,返回
            self.contactOrgDataIsSaveing=NO;
            return;
        }

        // <iq type="get">
        //   <query xmlns="com:yineng:orgget"/>
        // </iq>

        //req
        NSXMLElement *req = [NSXMLElement elementWithName:@"req" xmlns:@"com:yineng:orgget"];
        //params

        /**HX** 2019-3-21 修改，之前这个参数要控制是增量更新还是全量更新，现在获取数据后的全量删除再添加数据的操作已经不允许增量更新的办法了，所以这里固定为0 之前的逻辑代码见：YNedutStandard.1812.2.0分支 **/
        NSString *servertime = @"0";

        NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:servertime, @"servertime", nil];
        NSString *jsonStr = [YNIMTool jsonStringWithObject:dic];
        NSXMLElement *params = [NSXMLElement elementWithName:@"params" stringValue:jsonStr];
        [req addChild:params];

        XMPPJID *myJid = xmppStream.myJID;
        XMPPIQ *iq = [XMPPIQ iqWithType:@"get" elementID:[XMPPStream generateUUID]];
        [iq addAttributeWithName:@"from" stringValue:myJid.description];
        [iq addAttributeWithName:@"to" stringValue:[NSString stringWithFormat:@"admin@%@",myJid.domain]];
        [iq addChild:req];


        [xmppIDTracker addID:[iq elementID]
                      target:self
                    selector:@selector(handleFetchRosterQueryIQ:withInfo:)
                     timeout:YNXMPP_IQ_TIMEOUT+10];//这里的超时时间设长点不然数据多了会出问题

        [xmppStream sendElement:iq];

        [self _setRequestedRoster:YES];
    }};

    if (dispatch_get_specific(moduleQueueTag)) {
        block();
    }else {
        dispatch_async(moduleQueue, block);
    }
}
//手动获取全量组织机构信息
- (void)manualFetchRoster
{
    if (self.contactOrgDataIsSaveing == YES) {
        return;
    }
    self.contactOrgDataIsSaveing = YES;
    // This is a public method, so it may be invoked on any thread/queue.
    dispatch_block_t block = ^{ @autoreleasepool {


        NSXMLElement *req = [NSXMLElement elementWithName:@"req" xmlns:@"com:yineng:orgget"];


        NSString *servertime = [NSString stringWithFormat:@"0"]; //从数据库取出servertime。

        NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:servertime, @"servertime", nil];
        NSString *jsonStr = [YNIMTool jsonStringWithObject:dic];
        NSXMLElement *params = [NSXMLElement elementWithName:@"params" stringValue:jsonStr];
        [req addChild:params];

        XMPPJID *myJid = xmppStream.myJID;
        XMPPIQ *iq = [XMPPIQ iqWithType:@"get" elementID:[XMPPStream generateUUID]];
        [iq addAttributeWithName:@"from" stringValue:myJid.description];
        [iq addAttributeWithName:@"to" stringValue:[NSString stringWithFormat:@"admin@%@",myJid.domain]];
        [iq addChild:req];
        debugLog(@"经过");
        [xmppIDTracker addID:[iq elementID]
                      target:self
                    selector:@selector(handleManualFetchRosterQueryIQ:withInfo:)
                     timeout:YNXMPP_IQ_TIMEOUT+10];//这里的超时时间设长点不然数据多了会出问题

        [xmppStream sendElement:iq];


    }};

    if (dispatch_get_specific(moduleQueueTag)) {
        block();
    }else {
        dispatch_async(moduleQueue, block);
    }
}

//获取用户在线状态
- (void)fetchUserStatus
{
    dispatch_block_t block = ^{ @autoreleasepool {

      /*
       <iq id="URRYf-31" type="set" from="xiongshihui@m.com/Spark 2.6.3" to="admin@m.com"><req xmlns="com:yineng:status">
       <params>{"userNoList":["xiongshihui","wangx"]}</params></req></iq>
       */
        //req
        NSXMLElement *req = [NSXMLElement elementWithName:@"req" xmlns:@"com:yineng:status"];
        //params
        NSXMLElement *params = [NSXMLElement elementWithName:@"params" stringValue:@"{}"];
        [req addChild:params];

        //iq
        XMPPJID *myJid = xmppStream.myJID;
        XMPPIQ *iq = [XMPPIQ iqWithType:@"set" elementID:[XMPPStream generateUUID]];
        [iq addAttributeWithName:@"from" stringValue:myJid.description];
        [iq addAttributeWithName:@"to" stringValue:[NSString stringWithFormat:@"admin@%@",myJid.domain]];
        [iq addChild:req];

        [xmppIDTracker addID:[iq elementID]
                      target:self
                    selector:@selector(handleUserStatusQueryIQ:withInfo:)
                     timeout:YNXMPP_IQ_TIMEOUT];

        [xmppStream sendElement:iq];


    }};

    if (dispatch_get_specific(moduleQueueTag)) {
        block();
    }else {
        dispatch_async(moduleQueue, block);
    }
}

/*
 * 获取初始化信息
 */
- (void)MessageInit
{
    dispatch_block_t block = ^{ @autoreleasepool {

        /*请求IQ样式
        <iq id="URRYf-31"
            type="get"
            from="xiongshihui@m.com/Spark 2.6.3"
            to="admin@m.com">
            <req xmlns="com:yineng:clientinit">
                <params>{}</params>
            </req>
         </iq>
        */

        //req

        NSXMLElement *req = [NSXMLElement elementWithName:@"req" xmlns:@"com:yineng:clientinit"];

        //params
        NSDictionary *dic = [NSDictionary dictionary];


        NSString *jsonStr = [YNIMTool jsonStringWithObject:dic];
        NSXMLElement *params = [NSXMLElement elementWithName:@"params" stringValue:jsonStr];
        [req addChild:params];

        //iq
        XMPPJID *myJid = xmppStream.myJID;
        XMPPIQ *iq = [XMPPIQ iqWithType:@"get" elementID:[XMPPStream generateUUID]];
        [iq addAttributeWithName:@"from" stringValue:myJid.description];
        [iq addAttributeWithName:@"to" stringValue:[NSString stringWithFormat:@"admin@%@",myJid.domain]];
        [iq addChild:req];

        [xmppIDTracker addID:[iq elementID]
                      target:self
                    selector:@selector(handlefMessageInitQueryIQ:withInfo:)
                     timeout:YNXMPP_IQ_TIMEOUT];

        [xmppStream sendElement:iq];
    }};

    if (dispatch_get_specific(moduleQueueTag)) {
        block();
    }else {
        dispatch_async(moduleQueue, block);
    }

}

/**
 * 获取群信息以及群成员
 * 包括群与用户的关系，群列表
 **/
- (void)fetchGroupInfo
{
    dispatch_block_t block = ^{ @autoreleasepool{

        /*
         <iq id="URRYf-31" type="get" from="xiongshihui@m.com/Spark 2.6.3" to="admin@m.com"><req xmlns="com:yineng:group"><action></action><params>{"groupName":""}</params></req></iq>
         */

        //req
        NSXMLElement *req = [NSXMLElement elementWithName:@"req" xmlns:@"com:yineng:group"];

        //params
        NSDictionary *dic = [NSDictionary dictionaryWithObject:@"" forKey:@"groupName"];
        NSString *jsonStr = [YNIMTool jsonStringWithObject:dic];
        NSXMLElement *params = [NSXMLElement elementWithName:@"params" stringValue:jsonStr];
        [req addChild:params];
        NSXMLElement *action = [NSXMLElement elementWithName:@"action" numberValue:[NSNumber numberWithInt:8]];
        [req addChild:action];

        XMPPJID *myJid = xmppStream.myJID;
        //iq
        XMPPIQ *iq = [XMPPIQ iqWithType:@"get" elementID:[XMPPStream generateUUID]];
        [iq addAttributeWithName:@"from" stringValue:myJid.description];
        [iq addAttributeWithName:@"to" stringValue:[NSString stringWithFormat:@"admin@%@",myJid.domain]];
        [iq addChild:req];

        [xmppIDTracker addID:[iq elementID]
                      target:self
                    selector:@selector(handleGroupMemberQueryIQ:withInfo:)
                     timeout:YNXMPP_IQ_TIMEOUT];

        [xmppStream sendElement:iq];

    }};

    if (dispatch_get_specific(moduleQueueTag)) {

        block();

    }else {

        dispatch_async(moduleQueue, block);
    }
}

/**
 * 获取讨论组信息及成员
 * 包括讨论组与用户的关系，讨论组列表
 */
- (void)fetchDiscussionGroupInfo
{
    dispatch_block_t block = ^{ @autoreleasepool{

        /*
         <iq id="URRYf-31" type="get" from="xiongshihui@m.com/Spark 2.6.3" to="admin@m.com"><req xmlns="com:yineng:group"><action></action><params>{"groupName":""}</params></req></iq>
         */

        //req
        NSXMLElement *req = [NSXMLElement elementWithName:@"req" xmlns:@"com:yineng:group"];

        //params
        NSDictionary *dic = [NSDictionary dictionaryWithObject:@"" forKey:@"groupName"];
        NSString *jsonStr = [YNIMTool jsonStringWithObject:dic];
        NSXMLElement *params = [NSXMLElement elementWithName:@"params" stringValue:jsonStr];
        [req addChild:params];
        NSXMLElement *action = [NSXMLElement elementWithName:@"action" numberValue:[NSNumber numberWithInt:9]];
        [req addChild:action];

        XMPPJID *myJid = xmppStream.myJID;
        //iq
        XMPPIQ *iq = [XMPPIQ iqWithType:@"get" elementID:[XMPPStream generateUUID]];
        [iq addAttributeWithName:@"from" stringValue:myJid.description];
        [iq addAttributeWithName:@"to" stringValue:[NSString stringWithFormat:@"admin@%@",myJid.domain]];
        [iq addChild:req];

        [xmppIDTracker addID:[iq elementID]
                      target:self
                    selector:@selector(handleDiscussionMemberQueryIQ:withInfo:)
                     timeout:YNXMPP_IQ_TIMEOUT];

        [xmppStream sendElement:iq];

    }};

    if (dispatch_get_specific(moduleQueueTag)) {

        block();

    }else {

        dispatch_async(moduleQueue, block);
    }
}

/**
 漫游删除会话列表的会话消息
 黄旭 2017-12-13
 @param messageType 消息类型
 @param chatId 二人会话对方的id，讨论组/群id，广播消息没有删除操作
 */
-(void)deleteRoamMessageListWithChatType:(ReceiptMessageType)messageType chatId:(NSString*)chatId{
    dispatch_block_t block = ^{ @autoreleasepool{
        //req
        NSXMLElement *req = [NSXMLElement elementWithName:@"req" xmlns:@"com:yineng:chathistory"];
        //action
        NSXMLElement *action = [NSXMLElement elementWithName:@"action" stringValue:@"4"];
        [req addChild:action];

        NSMutableDictionary*paramsDic=[NSMutableDictionary dictionary];
        [paramsDic setObject:[NSString stringWithFormat:@"%ld",messageType] forKey:@"chatType"];
        if ([YNIMTool isEmptyString:chatId]) {
            [paramsDic setObject:@"" forKey:@"chatId"];
        }else{
            [paramsDic setObject:chatId forKey:@"chatId"];
        }
        //params
        NSXMLElement *params = [NSXMLElement elementWithName:@"params" stringValue:[YNIMTool jsonStringWithObject:paramsDic]];
        [req addChild:params];

        XMPPJID *myJid = xmppStream.myJID;
        //iq
        XMPPIQ *iq = [XMPPIQ iqWithType:@"get" elementID:[XMPPStream generateUUID]];
        [iq addAttributeWithName:@"from" stringValue:myJid.description];
        [iq addAttributeWithName:@"to" stringValue:[NSString stringWithFormat:@"admin@%@",myJid.domain]];
        [iq addChild:req];

        [xmppIDTracker addID:[iq elementID]
                      target:self
                    selector:@selector(deleteRoamMessageList:withInfo:)
                     timeout:YNXMPP_IQ_TIMEOUT];

        [xmppStream sendElement:iq];

    }};

    if (dispatch_get_specific(moduleQueueTag)) {

        block();

    }else {

        dispatch_async(moduleQueue, block);
    }
}

/**
 撤回消息，发送IQ消息
 2018-11-28JC添加

 @param messageType 消息类型
 @param chatId 二人会话对方的id，讨论组/项目组群id，广播消息没有删除操作
 @param packetId 消息id
 @param sendTime 时间
 */
-(void)updateWithdrawMessageListWithChatType:(ReceiptMessageType)messageType chatId:(NSString*)chatId packetId:(NSString *)packetId sendTime:(NSString *)sendTime{
    dispatch_block_t block = ^{ @autoreleasepool{
        //req
        NSXMLElement *req = [NSXMLElement elementWithName:@"req" xmlns:@"com:yineng:chathistory"];
        //action
        NSXMLElement *action = [NSXMLElement elementWithName:@"action" stringValue:@"5"];
        [req addChild:action];

        NSMutableDictionary*paramsDic=[NSMutableDictionary dictionary];
        [paramsDic setObject:[NSString stringWithFormat:@"%ld",messageType] forKey:@"chatType"];
        [paramsDic setObject:chatId forKey:@"chatId"];
        [paramsDic setObject:packetId forKey:@"msgId"];
        [paramsDic setObject:sendTime forKey:@"sendTime"];
        //params
        NSXMLElement *params = [NSXMLElement elementWithName:@"params" stringValue:[YNIMTool jsonStringWithObject:paramsDic]];
        [req addChild:params];

        XMPPJID *myJid = xmppStream.myJID;
        //iq
        XMPPIQ *iq = [XMPPIQ iqWithType:@"get" elementID:[XMPPStream generateUUID]];
        [iq addAttributeWithName:@"from" stringValue:myJid.description];
        [iq addAttributeWithName:@"to" stringValue:[NSString stringWithFormat:@"admin@%@",myJid.domain]];
        [iq addChild:req];

        [xmppIDTracker addID:[iq elementID]
                      target:self
                    selector:@selector(updateWithdrawMessageChat:withInfo:)
                     timeout:YNXMPP_IQ_TIMEOUT];

        [xmppStream sendElement:iq];

    }};

    if (dispatch_get_specific(moduleQueueTag)) {

        block();

    }else {

        dispatch_async(moduleQueue, block);
    }
}


/**
 获取漫游消息记录
 黄旭 2017-12-04
 @param messageType 消息类型
 @param chatId 二人会话对方的id，讨论组/群id，广播传空
 @param msgCount 拉取消息条数 正数为向上拉取，负数为向下拉取
 @param sendTime 消息时间，为空时拉取最新的几条数据，格式为："2017-11-30 17:12:12.20" 精确到毫秒
 @param isMessageNewListDat YES:消息列表拉取的消息漫游，NO：不是消息列表拉取的消息漫游
 */
-(void)getRoamMessageRecordWithChatType:(ReceiptMessageType)messageType chatId:(NSString*)chatId msgCount:(NSInteger)msgCount sendTime:(NSString*)sendTime isMessageNewListDat:(BOOL)isMessageNewListDat{

    if ([YNIMTool isEmptyString:chatId]) {
        chatId=@"";
    }
    if ([YNIMTool isEmptyString:sendTime]) {
        sendTime=@"";
    }
    dispatch_block_t block = ^{ @autoreleasepool{
        //req
        NSXMLElement *req = [NSXMLElement elementWithName:@"req" xmlns:@"com:yineng:chathistory"];
        //action
        NSXMLElement *action = [NSXMLElement elementWithName:@"action" stringValue:@"2"];
        [req addChild:action];

        NSMutableDictionary*paramsDic=[NSMutableDictionary dictionary];
        [paramsDic setObject:[NSString stringWithFormat:@"%ld",messageType] forKey:@"chatType"];
        [paramsDic setObject:chatId forKey:@"chatId"];
        [paramsDic setObject:[NSString stringWithFormat:@"%ld",msgCount] forKey:@"msgCount"];
        [paramsDic setObject:sendTime forKey:@"sendTime"];
        //params
        NSXMLElement *params = [NSXMLElement elementWithName:@"params" stringValue:[YNIMTool jsonStringWithObject:paramsDic]];
        [req addChild:params];

        XMPPJID *myJid = xmppStream.myJID;
        //iq
        XMPPIQ *iq = [XMPPIQ iqWithType:@"get" elementID:[XMPPStream generateUUID]];
        [iq addAttributeWithName:@"from" stringValue:myJid.description];
        [iq addAttributeWithName:@"to" stringValue:[NSString stringWithFormat:@"admin@%@",myJid.domain]];
        [iq addChild:req];

        if(isMessageNewListDat)
        {
            [xmppIDTracker addID:[iq elementID]
                          target:self
                        selector:@selector(getAitRoamMessageRecordQueryIQ:withInfo:)
                         timeout:YNXMPP_IQ_TIMEOUT];
        }
        else
        {

            [xmppIDTracker addID:[iq elementID]
                          target:self
                        selector:@selector(getRoamMessageRecordQueryIQ:withInfo:)
                         timeout:YNXMPP_IQ_TIMEOUT];
        }

        [xmppStream sendElement:iq];

    }};

    if (dispatch_get_specific(moduleQueueTag)) {

        block();

    }else {

        dispatch_async(moduleQueue, block);
    }
}

/**HX** 同步会话列表 **/
- (void)synchronizationSessionList
{
    dispatch_block_t block = ^{ @autoreleasepool{

        /*
         <iq id="URRYf-31" type="get" from="xiongshihui@m.com/Spark 2.6.3" to="admin@m.com"><req xmlns="com:yineng:chathistory"><action>1</action></req></iq>
         */

        //req
        NSXMLElement *req = [NSXMLElement elementWithName:@"req" xmlns:@"com:yineng:chathistory"];
        //action
        NSXMLElement *action = [NSXMLElement elementWithName:@"action" stringValue:@"1"];
        [req addChild:action];

        XMPPJID *myJid = xmppStream.myJID;
        //iq
        XMPPIQ *iq = [XMPPIQ iqWithType:@"get" elementID:[XMPPStream generateUUID]];
        [iq addAttributeWithName:@"from" stringValue:myJid.description];
        [iq addAttributeWithName:@"to" stringValue:[NSString stringWithFormat:@"admin@%@",myJid.domain]];
        [iq addChild:req];

        [xmppIDTracker addID:[iq elementID]
                      target:self
                    selector:@selector(handleSessionListQueryIQ:withInfo:)
                     timeout:YNXMPP_IQ_TIMEOUT];

        [xmppStream sendElement:iq];

    }};

    if (dispatch_get_specific(moduleQueueTag)) {

        block();

    }else {

        dispatch_async(moduleQueue, block);
    }

}


#pragma mark someMothed

//搜索自己的账户
- (void)searchMeInfomationWithNo:(NSString*)userNo
{
    dispatch_block_t block = ^{ @autoreleasepool{

        /*
         <iq id="URRYf-31"
         type="get"
         from="xiongshihui@m.com/Spark 2.6.3"
         to="admin@m.com">
         <req xmlns="com:yineng:querydetail">
         <params>
         {"userNo":""}
         </params>
         </req>
         </iq>
         */

        //req
        NSXMLElement *req = [NSXMLElement elementWithName:@"req" xmlns:@"com:yineng:querydetail"];

        //params
        if ([YNIMTool isEmptyString:userNo]) {
            return;
        }
        NSArray *infoArray = @[userNo];
        NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:infoArray, @"userNoList",self.companycode,@"code",@"1",@"appflag", nil];

        NSString *jsonStr = [YNIMTool jsonStringWithObject:dic];
        NSXMLElement *params = [NSXMLElement elementWithName:@"params" stringValue:jsonStr];
        [req addChild:params];

        XMPPJID *myJid = xmppStream.myJID;
        //iq
        XMPPIQ *iq = [XMPPIQ iqWithType:@"get" elementID:[XMPPStream generateUUID]];
        [iq addAttributeWithName:@"from" stringValue:myJid.description];
        [iq addAttributeWithName:@"to" stringValue:[NSString stringWithFormat:@"admin@%@",myJid.domain]];
        [iq addChild:req];

        __weak typeof(self) weakSelf = self;
        [xmppIDTracker addID:[iq elementID] block:^(XMPPIQ *iq, id<XMPPTrackingInfo> info){

            [weakSelf handleSearchResultIq:iq];

        } timeout:4];

        [xmppStream sendElement:iq];

    }};

    if (dispatch_get_specific(moduleQueueTag)) {

        block();

    }else {

        dispatch_async(moduleQueue, block);
    }

}

/**
 * 修改本人信息，只能修改电话号码和电子邮件
 * jid是自己的jid,只能是自己的
 * dic 是修改信息的字典 例如：
 * {"telephone":"13800000000","email":".moumou.yn.com.cn"}
 *
 **/
- (void)modifyUsInfoTo:(XMPPJID *)jid detailedInfo:(NSDictionary *)dic
{

    dispatch_block_t block = ^{ @autoreleasepool {

        /*
         <iq id="URRYf-31"
         type="set"
         from="xiongshihui@m.com/Spark 2.6.3"
         to="admin@m.com">
         <req
         xmlns="com:yineng:updatedetail">
         <params>
         {"telephone":"13800000000"}
         </params>
         </req>
         </iq>
         */

        //req
        NSXMLElement *req = [NSXMLElement elementWithName:@"req" xmlns:@"com:yineng:updatedetail"];

        //params
        NSString *jsonStr = [YNIMTool jsonStringWithObject:dic];
        NSXMLElement *params = [NSXMLElement elementWithName:@"params" stringValue:jsonStr];
        [req addChild:params];

        XMPPJID *myJid = xmppStream.myJID;

        //iq
        XMPPIQ *iq = [XMPPIQ iqWithType:@"set" elementID:[XMPPStream generateUUID]];
        [iq addAttributeWithName:@"from" stringValue:myJid.description];
        [iq addAttributeWithName:@"to" stringValue:[NSString stringWithFormat:@"admin@%@",myJid.domain]];
        [iq addChild:req];

        [xmppIDTracker addID:[iq elementID]
                      target:self
                    selector:@selector(handlefModifyQueryIQ:withInfo:)
                     timeout:YNXMPP_IQ_TIMEOUT];

        [xmppStream sendElement:iq];
    }};

    if (dispatch_get_specific(moduleQueueTag)) {

        block();

    }else {

        dispatch_async(moduleQueue, block);
    }

}

- (void)handleSearchResultIq:(XMPPIQ *)iq
{
    if (iq)
    {
        NSXMLElement *req = [iq elementForName:@"req" xmlns:@"com:yineng:querydetail"];

        int code = [[req elementForName:@"code"] stringValueAsInt];

        if (code == 200)
        {
            NSXMLElement *resp = [req elementForName:@"resp"];
            NSArray *arr = (NSArray*)[YNIMTool getJsonObjectFromJsonString:[resp stringValue]];
            NSDictionary *dic = [arr lastObject];
            NSString *userNumber = [dic objectForKey:@"userNo"];

            if ([self.userId isEqualToString:userNumber])
            {
                //保存用户信息
                debugLog(@"openfire返回的用户信息：%@",dic);
                //本地的唯一标识符和获得的相同的话
                [self MessageInit];

            }
            else
            {
                //不相同, 退出登录用userID登录
                dispatch_async(dispatch_get_main_queue(), ^{
                    if ([self.delegate respondsToSelector:@selector(xmppStreamDidDisconnect:)]) {
                        [self.delegate xmppStreamDidDisconnect:2];
                    }
                });
            }
        }else{
            /**HX** 2017-3-31,解决不存在用户登录不报错也登不上的bug，添加错误code码的判断以解决 **/
            dispatch_async(dispatch_get_main_queue(), ^{
                if ([self.delegate respondsToSelector:@selector(xmppStreamDidDisconnect:)]) {
                    [self.delegate xmppStreamDidDisconnect:2];
                }
            });
        }
    }
}

/**
 * 查找用户或获取某个用户的详细信息
 * 接口文档里面是根据UserNo获取信息
 * 这里是通过JID获取信息获取信息，等实现的时候决定用哪种
 **/

- (void)searchDetailedInfomationOfAUser:(NSString *)userId
{
    dispatch_block_t block = ^{ @autoreleasepool{

        /*
         <iq id="URRYf-31"
         type="get"
         from="xiongshihui@m.com/Spark 2.6.3"
         to="admin@m.com">
         <req xmlns="com:yineng:querydetail">
         <params>
         {"userNo":""}
         </params>
         </req>
         </iq>
         */

        //req
        NSXMLElement *req = [NSXMLElement elementWithName:@"req" xmlns:@"com:yineng:querydetail"];

        //params
        NSString *userNo = userId;
        if ([YNIMTool isEmptyString:userNo]) {
            dispatch_async(dispatch_get_main_queue(), ^{
                if ([self.delegate respondsToSelector:@selector(ynslRosterDidSearchInfo:)]) {
                    [self.delegate ynslRosterDidSearchInfo:nil];
                }
            });
            return;
        }
        NSArray *infoArray = @[userNo];
        NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:infoArray, @"userNoList",self.companycode,@"code",@"1",@"appflag", nil];

        NSString *jsonStr = [YNIMTool jsonStringWithObject:dic];
        NSXMLElement *params = [NSXMLElement elementWithName:@"params" stringValue:jsonStr];
        [req addChild:params];

        XMPPJID *myJid = xmppStream.myJID;
        //iq
        XMPPIQ *iq = [XMPPIQ iqWithType:@"get" elementID:[XMPPStream generateUUID]];
        [iq addAttributeWithName:@"from" stringValue:myJid.description];
        [iq addAttributeWithName:@"to" stringValue:[NSString stringWithFormat:@"admin@%@",myJid.domain]];
        [iq addChild:req];

        [xmppIDTracker addID:[iq elementID]
                      target:self
                    selector:@selector(handleSearchDetailedQueryIQ:withInfo:)
                     timeout:2];

        [xmppStream sendElement:iq];

    }};

    if (dispatch_get_specific(moduleQueueTag)) {

        block();

    }else {

        dispatch_async(moduleQueue, block);
    }

}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark XMPPIDTracker
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 *
 * 获取组织机构数据, 组织机构包含5个部分.
 * <req xmlns="com:yineng:orgget" </req>.
 * type                          result.
 *
 * resp element, 数据类型为JSON, 组织机构包含5个部分:
 * servertime    为服务器当前时间戳。
 * orgList       为用户相关的组织机构详细信息。
 * userList      为用户相关的用户基本信息。
 * relist        为用户与组织机构关系数据。
 * statusList    为当前在线用户状态。
 * 列表中每个属性的含义请查看数据字典。
 *
 * code element: 数据类型为String, 返回码, 200为成功。
 *
 */
- (void)handleFetchRosterQueryIQ:(XMPPIQ *)iq withInfo:(XMPPBasicTrackingInfo *)basicTrackingInfo{

    dispatch_block_t block = ^{ @autoreleasepool {

        /** iq接收样式
        <iq type="result"
            id="URRYf-31"
            from="admin@m.com"
            to="xiongshihui@m.com/Spark 2.6.3">
            <req xmlns="com:yineng:orgget ">
                <resp>
                    {"servertime":"","orgList":[{},{}],"userList":[{},{}],"relList ":[{},{}],"statusList":[{}]}
                </resp>
                <code>
                    200
                </code>
            </req>
        </iq>
         **/

        //req
        NSXMLElement *req = [iq elementForName:@"req" xmlns:@"com:yineng:orgget"];

        //code
        int code = [[req elementForName:@"code"] stringValueAsInt];

        //200为成功返回数据, 其他数据不成功，不需要调用下面方法
        if (code == 200) {

            BOOL hasRoster = [self hasRoster];
            NSXMLElement *resp = [req elementForName:@"resp"];

            NSDictionary *dic = (NSDictionary*)[YNIMTool getJsonObjectFromJsonString:[resp stringValue]];

            if (!hasRoster)
            {
                //开始填充
                [self _setPopulatingRoster:YES];
                WEAKSELF
                dispatch_async(dispatch_get_global_queue(0, 0), ^{
                    //2016-9-9jc修改，解决后台删除组织机构，人员后，客服端需要逻辑删除组织机构和人员信息
                    //先做所有逻辑删除组织机构，人员信息，然后更新到多少组织机构和人员信息再做逻辑更新操作
                    BOOL result = [YNContactManager logicDeleteAllUserAndOrgTree];
                    if(result)
                    {
                        //保存组织机构信息
                        [YNContactManager saveContactOrgWithOrgDictionary:dic];
                        debugLog(@"登录收取数据 第二步：获取群组信息 获取讨论组信息");
                        [weakSelf fetchGroupInfo];
                        [weakSelf fetchDiscussionGroupInfo];
                        dispatch_async(dispatch_get_main_queue(), ^{
                            if ([weakSelf.delegate respondsToSelector:@selector(getOrgAndUserDataSaveFinished:)]) {
                                [weakSelf.delegate getOrgAndUserDataSaveFinished:dic];
                            }
                        });
                    }else{
                        //保存组织机构、用户等信息失败
                        dispatch_async(dispatch_get_main_queue(), ^{
                            if ([weakSelf.delegate respondsToSelector:@selector(getOrgAndUserDataSaveFail)]) {
                                [weakSelf.delegate getOrgAndUserDataSaveFail];
                            }
                        });
                    }
                    
                    self.contactOrgDataIsSaveing=NO;
                });
                [self _setHasRoster:YES];
                //填充数据库结束
                [self _setPopulatingRoster:NO];
            }else{
                //保存组织机构、用户等信息失败
                dispatch_async(dispatch_get_main_queue(), ^{
                    if ([self.delegate respondsToSelector:@selector(getOrgAndUserDataSaveFail)]) {
                        [self.delegate getOrgAndUserDataSaveFail];
                    }
                });
                self.contactOrgDataIsSaveing=NO;
            }
        }else{
            //获取组织机构、用户等信息失败
            dispatch_async(dispatch_get_main_queue(), ^{
                if ([self.delegate respondsToSelector:@selector(getOrgAndUserDataSaveFail)]) {
                    [self.delegate getOrgAndUserDataSaveFail];
                }
            });
            self.contactOrgDataIsSaveing=NO;
        }
    }};

    if (dispatch_get_specific(moduleQueueTag))
        block();
    else
        dispatch_async(moduleQueue, block);

}

/**
 *
 * 获得初始化信息回调.
 * <req xmlns="com:yineng:orgget" </req>.
 * type                          result.
 *
 * resp element, 数据类型为JSON, 下面为包含信息:
 * show_orgtype_name             为组织机构类型的显示名称;
 * group_max_user                为群的最大人数;
 * disgroup_max_user             为讨论组的最大人数;
 * org_update_type               为组织机构数据更新方式，如果为0则表示全量更新，1则表示增量更新;
 * max_group_can_create          表示可创建的最大群个数;
 * max_disdisgroup_can_create    为可创建的最大讨论组个数.
 *
 * code element, 数据类型为String:   返回码,200为成功。
 *
 */
- (void)handlefMessageInitQueryIQ:(XMPPIQ *)iq withInfo:(XMPPBasicTrackingInfo *)basicTrackingInfo
{
    dispatch_block_t block = ^{ @autoreleasepool {

        /** iq接受样式
        <iq type="result"
            id="URRYf-31"
            from="admin@m.com"
            to="xiongshihui@m.com/Spark 2.6.3">
            <req xmlns="com:yineng:orgget ">
                <resp>
                  {"show_orgtype_name":"2|行政班&3|教学班","group_max_user":"200","disgroup_max_user":"50"}
                </resp>
                <code>
                  200
                </code>
            </req>
        </iq>
         **/

        //req
        NSXMLElement *req = [iq elementForName:@"req" xmlns:@"com:yineng:clientinit"];

        //响应码
        int code = [[req elementForName:@"code"] stringValueAsInt];

        //判断相应是否成功, 200为成功
        if (code == 200) {
            //来自哪里
            [[iq attributeForName:@"from"] stringValue];

            //发送到哪里
            [[iq attributeForName:@"to"] stringValue];

            //返回Json字典信息
            NSDictionary *resp = [YNIMTool getJsonObjectFromJsonString:[[req elementForName:@"resp"] stringValue]];

            if(resp != nil) {

                //这里处理JSON字典, 需要保存的就保存，不需要的不管
                //将初始化信息存入个人文件
                NSXMLElement *resp = [req elementForName:@"resp"];

                NSDictionary *dic = (NSDictionary*)[YNIMTool getJsonObjectFromJsonString:[resp stringValue]];

                if (dic!=nil)
                {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        if ([self.delegate respondsToSelector:@selector(getLoginUserInitInforSucceed:)]) {
                            [self.delegate getLoginUserInitInforSucceed:dic];
                        }
                    });
                }
            }
        }
    }};

    if (dispatch_get_specific(moduleQueueTag))
        block();
    else
        dispatch_async(moduleQueue, block);
}



- (void)handleUserStatusQueryIQ:(XMPPIQ *)iq withInfo:(XMPPBasicTrackingInfo *)basicTrackingInfo
{
    dispatch_block_t block = ^{ @autoreleasepool {

        /** iq接受样式
         <iq type="result" id="URRYf-31" from="admin@m.com" to="xiongshihui@m.com/Spark 2.6.3"><req xmlns="com:yineng: updatedetail"><resp>{"statusList":[{"resources":[{"resource":"Spark 2.6.3","status":"online","show":"android"}],"userNo":"xiongshihui"}]}</resp><code>200</code></req></iq>
         **/

        //req
        NSXMLElement *req = [iq elementForName:@"req" xmlns:@"com:yineng:status"];

        //响应码
        int code = [[req elementForName:@"code"] stringValueAsInt];

        //判断相应是否成功, 200为成功
        if (code == 200) {
            NSXMLElement *resp = [req elementForName:@"resp"];

            NSDictionary *dic =(NSDictionary*)[YNIMTool getJsonObjectFromJsonString:[resp stringValue]];
            //保存到数据库
            [YNContactManager clearUserOnlineStatusAtTheTimeOfOffline];
            WEAKSELF
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                [YNContactManager updateAllUserStatus:dic];
                debugLog(@"登录收取数据 第四步：同步会话列表和拉取应用首页数据");
                [weakSelf synchronizationSessionList];
            });

        }else{
            debugLog(@"登录时获取用户状态信息失败");
            debugLog(@"登录收取数据 第四步：同步会话列表和拉取应用首页数据");
            [self synchronizationSessionList];
        }
    }};

    if (dispatch_get_specific(moduleQueueTag))
        block();
    else
        dispatch_async(moduleQueue, block);
}
/**
 *
 * 获取修改信息IQ。
 *
 * code element:    数据类型为String, 返回码, 200为成功.
 *
 * resp element:    数据类型为 JSON, 返回参数为JSON字典, 一个完整的用户对象.
 *
 */
- (void)handlefModifyQueryIQ:(XMPPIQ *)iq withInfo:(XMPPBasicTrackingInfo *)basicTrackingInfo
{
    dispatch_block_t block = ^{ @autoreleasepool {

        /** iq接受样式
         <iq type="result"
         id="URRYf-31"
         from="admin@m.com"
         to="xiongshihui@m.com/Spark 2.6.3">
         <req xmlns="com:yineng: updatedetail">
         <resp>{}</resp>
         <code>200</code>
         </req>
         </iq>
         **/

        //req
        NSXMLElement *req = [iq elementForName:@"req" xmlns:@"com:yineng:updatedetail"];

        //响应码
        int code = [[req elementForName:@"code"] stringValueAsInt];

        //判断相应是否成功, 200为成功
        if (code == 200) {
            NSXMLElement *resp = [req elementForName:@"resp"];

            NSDictionary *dic = (NSDictionary*)[YNIMTool getJsonObjectFromJsonString:[resp stringValue]];

            dispatch_async(dispatch_get_main_queue(), ^{
                if ([self.delegate respondsToSelector:@selector(ynslRosterDidSearchInfo:)]) {
                    [self.delegate ynslRosterDidSearchInfo:dic];
                }
            });
        }

    }};

    if (dispatch_get_specific(moduleQueueTag))
        block();
    else
        dispatch_async(moduleQueue, block);
}

/**
 *
 * 获取查找信息IQ。
 *
 * code element:    数据类型为String, 返回码, 200为成功.
 *
 * resp element:    数据类型为 JSON, 返回参数为JSON字典, 一个完整的用户对象.
 *
 */
- (void)handleSearchDetailedQueryIQ:(XMPPIQ *)iq withInfo:(XMPPBasicTrackingInfo *)basicTrackingInfo
{
    dispatch_block_t block = ^{ @autoreleasepool {

        /** iq接收样式
         <iq xmlns="jabber:client" type="result" id="29523931-F575-4DF5-B538-BA5324707CAA" to="t00040@m.com/Msg_Phone" from="admin@m.com"><req xmlns="com:yineng:querydetail"><code>200</code><resp>{"createTime":1401759915000,"dayOfBirth":"1974/3/10","email":"teacher@yineng.com.cn","gender":1,"headUrl":"","joinTime":"1998/7/15","ordId":0,"post":"老师","removeTag":0,"signature":"","telephone":"13500000000","userName":"teacher_00040","userNo":"t00040","userType":1}</resp></req></iq>
         **/


        //req
        NSXMLElement *req = [iq elementForName:@"req" xmlns:@"com:yineng:querydetail"];

        //响应码
        int code = [[req elementForName:@"code"] stringValueAsInt];

        //判断相应是否成功, 200为成功
        if (code == 200) {
            NSXMLElement *resp = [req elementForName:@"resp"];

            NSArray *arr = (NSArray*)[YNIMTool getJsonObjectFromJsonString:[resp stringValue]];
            NSDictionary *dic = [arr lastObject];

            //更新界面
            dispatch_async(dispatch_get_main_queue(), ^{
                if ([self.delegate respondsToSelector:@selector(ynslRosterDidSearchInfo:)]) {
                    [self.delegate ynslRosterDidSearchInfo:dic];
                }
            });
        }
        else
        {
            dispatch_async(dispatch_get_main_queue(), ^{
                if ([self.delegate respondsToSelector:@selector(ynslRosterDidSearchInfo:)]) {
                    [self.delegate ynslRosterDidSearchInfo:nil];
                }
            });
        }

    }};

    if (dispatch_get_specific(moduleQueueTag))
        block();
    else
        dispatch_async(moduleQueue, block);

}

/**
 *
 * 获取群成员和群信息。
 *
 * code element:    数据类型为String, 返回码, 200为成功.
 *
 * resp element:    数据类型为 JSON, 返回参数为JSON字典
 * roomList:        群列表. creatTime(创建时间); desc(创建人和详细信息);
 *                         groupName(群ID,唯一标识符); maxUsers(最大人数);
 *                         naturalName(群名字); subject(应该是管理员)。
 * userList:        用户与群的关系。groupName:(群ID); jid(用户唯一ID)
 *                              role:(用户角色:10-创建人 20-管理员 50-一般用户)。
 */
- (void)handleGroupMemberQueryIQ:(XMPPIQ *)iq withInfo:(XMPPBasicTrackingInfo *)basicTrackingInfo
{
    /*
    <iq xmlns="jabber:client" type="result" id="22CF7C01-164F-48A3-A888-4A5548561736" to="t00040@m.com/Msg_Phone" from="admin@m.com"><req xmlns="com:yineng:group"><code>200</code><action>8</action><resp>{"roomList":[],"userList":[]}</resp></req></iq>
     */

    dispatch_block_t block = ^{ @autoreleasepool {

        //req
        NSXMLElement *req = [iq elementForName:@"req" xmlns:@"com:yineng:group"];

        //响应码
        int code = [[req elementForName:@"code"] stringValueAsInt];

        //判断响应是否成功, 200为成功
        if (code == 200)
        {
            NSDictionary *resp = [YNIMTool getJsonObjectFromJsonString:[[req elementForName:@"resp"] stringValue]];
            dispatch_async(dispatch_get_global_queue(0, 0), ^{

                //保存群信息
                [YNContactManager saveContactGroupWithGroupType:kContact_UserGroupType orgDictionary:resp];
                
                dispatch_async(dispatch_get_main_queue(), ^{
                    if ([self.delegate respondsToSelector:@selector(groupInforSaveFinished)]) {
                        [self.delegate groupInforSaveFinished];
                    }
                });

            });
        }
    }};

    if (dispatch_get_specific(moduleQueueTag))
        block();
    else
        dispatch_async(moduleQueue, block);

}

/**
 *
 * 获取讨论组成员和群信息。
 *
 * code element:    数据类型为String, 返回码, 200为成功.
 *
 * resp element:    数据类型为 JSON, 返回参数为JSON字典
 * roomList:        讨论组列表. creatTime(创建时间); desc(创建人和详细信息);
 *                         groupName(群ID,唯一标识符); maxUsers(最大人数);
 *                         naturalName(群名字); subject(应该是管理员)。
 * userList:        用户与讨论组的关系。groupName:(群ID); jid(用户唯一ID)
 *                                  role:(用户角色:10-创建人 20-管理员 50-一般用户)。
 */
- (void)handleDiscussionMemberQueryIQ:(XMPPIQ *)iq withInfo:(XMPPBasicTrackingInfo *)basicTrackingInfo
{
    /*
     <iq xmlns="jabber:client" type="result" id="B2A7F5F7-24FD-411C-87BB-8DA65E94BDCE" to="t00040@m.com/Msg_Phone" from="admin@m.com"><req xmlns="com:yineng:group"><code>200</code><action>9</action><resp>{"roomList":[],"userList":[]}</resp></req></iq>
     */

    dispatch_block_t block = ^{ @autoreleasepool {

        //req
        NSXMLElement *req = [iq elementForName:@"req" xmlns:@"com:yineng:group"];

        //响应码
        int code = [[req elementForName:@"code"] stringValueAsInt];

        //判断相应是否成功, 200为成功
        if (code == 200)
        {
            NSDictionary *resp = [YNIMTool getJsonObjectFromJsonString:[[req elementForName:@"resp"] stringValue]];
            WEAKSELF
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                //保存讨论组信息
                [YNContactManager saveContactGroupWithGroupType:kContact_DiscussionGropType orgDictionary:resp];
                debugLog(@"登录收取数据 第三步：获取用户状态信息");
                [weakSelf fetchUserStatus];
                
                dispatch_async(dispatch_get_main_queue(), ^{
                    if ([self.delegate respondsToSelector:@selector(discussionSaveFinished)]) {
                        [self.delegate discussionSaveFinished];
                    }
                    if ([self.delegate respondsToSelector:@selector(projectTeamSaveFinished)]) {
                        [self.delegate projectTeamSaveFinished];
                    }
                });

            });

        }else{
            //获取讨论组信息失败
            debugLog(@"登录时获取群、讨论组信息失败");
            debugLog(@"登录收取数据 第三步：获取用户状态信息");
            [self fetchUserStatus];
        }
    }};

    if (dispatch_get_specific(moduleQueueTag))
        block();
    else
        dispatch_async(moduleQueue, block);

}

/**
 *
 *  @brief  漫游删除会话列表的回调
 *
 *  @param iq
 *  @param basicTrackingInfo
 */
- (void)deleteRoamMessageList:(XMPPIQ *)iq withInfo:(XMPPBasicTrackingInfo *)basicTrackingInfo
{
    /*
     <iq type="result" id="URRYf-31" from="admin@m.com" to="xiongshihui@m.com/Spark 2.6.3"><req xmlns="com:yineng:offline "><resp>{"messageList":[{"sendTime":"","type":"","from":""}],"total":"150"}</resp><code>200</code></req></iq>
     */
    dispatch_block_t block = ^{ @autoreleasepool {

        //req
        NSXMLElement *req = [iq elementForName:@"req"];

        //响应码
        int code = [[req elementForName:@"code"] stringValueAsInt];

        //判断相应是否成功, 200为成功
        if (code == 200) {
            //删除成功
            dispatch_async(dispatch_get_main_queue(), ^{
                if ([self.delegate respondsToSelector:@selector(deleteRoamMessageListSuccess:)]) {
                    [self.delegate deleteRoamMessageListSuccess:YES];
                }
            });
        }else{
            //删除失败
            dispatch_async(dispatch_get_main_queue(), ^{
                if ([self.delegate respondsToSelector:@selector(deleteRoamMessageListSuccess:)]) {
                    [self.delegate deleteRoamMessageListSuccess:NO];
                }
            });
        }

    }};

    if (dispatch_get_specific(moduleQueueTag))
        block();
    else
        dispatch_async(moduleQueue, block);
}

/**
 *
 *  @brief  撤回消息的回调
 *
 *  @param iq
 *  @param basicTrackingInfo
 */
- (void)updateWithdrawMessageChat:(XMPPIQ *)iq withInfo:(XMPPBasicTrackingInfo *)basicTrackingInfo
{
    /*
     <iq type="result" id="URRYf-31" from="admin@m.com" to="xiongshihui@m.com/Spark 2.6.3"><req xmlns="com:yineng:offline "><resp>{"messageList":[{"sendTime":"","type":"","from":""}],"total":"150"}</resp><code>200</code></req></iq>
     */
    dispatch_block_t block = ^{ @autoreleasepool {

        //req
        NSXMLElement *req = [iq elementForName:@"req"];

        //响应码
        int code = [[req elementForName:@"code"] stringValueAsInt];

        if(code == 200)
        {
            debugLog(@"撤回成功");

            NSString *respStr = [[req elementForName:@"resp"] stringValue];
            if (![YNIMTool isEmptyString:respStr]) {
                NSDictionary *respDic=[YNIMTool getJsonObjectFromJsonString:respStr];
                if(![YNIMTool isEmptyDictionary:respDic])
                {
                    NSString * chatId = [respDic objectForKey:@"chatId"];
                    NSString * packetId = [respDic objectForKey:@"msgId"];
                    
                    //修改本地撤回状态
                    if(![YNIMTool isEmptyString:packetId] && ![YNIMTool isEmptyString:chatId])
                    {
                        
                        CurrenChatObjType groupType = [YNMessagerReceiveHandle getCurrentMessageChatTypemanyPeopleChatWith:chatId];
                        
                        BOOL result = NO;
                        if(groupType == 0)
                        {
                            result =  [YNMessageContactManager updateTwoPeopleChatWithdrawWithPackId:packetId];
                        }
                        else
                        {
                            
                            result= [YNMessageContactManager updateManyPeopleMessageWithdrawWithPacketId:packetId groupType:groupType];
                        }
                        //更新消息聊天界面UI
                        if(result)
                        {
                            dispatch_async(dispatch_get_main_queue(), ^{
                                if ([self.delegate respondsToSelector:@selector(messengerChatWithDrawUpdateMsgId:success:)]) {
                                    [self.delegate messengerChatWithDrawUpdateMsgId:packetId success:YES];
                                }
                            });
                        }
                        else
                        {
                            dispatch_async(dispatch_get_main_queue(), ^{
                                if ([self.delegate respondsToSelector:@selector(messengerChatWithDrawUpdateMsgId:success:)]) {
                                    [self.delegate messengerChatWithDrawUpdateMsgId:packetId success:NO];
                                }
                            });
                        }
                        
                        BOOL resultList = [YNMessageContactManager updateRecentChatWithdrawWithChatId:chatId packId:packetId];
                        //更新消息列表UI
                        if(resultList)
                        {
                            dispatch_async(dispatch_get_main_queue(), ^{
                                if ([self.delegate respondsToSelector:@selector(updataRecentChatInterface)]) {
                                    [self.delegate receiveChatMesWithRecentChatId:packetId userNo:nil];
                                }
                            });
                        }
                        
                    }
                    
                }
                
            }

        }
        else
        {
            debugLog(@"撤回失败：%d",code);
            dispatch_async(dispatch_get_main_queue(), ^{
                if ([self.delegate respondsToSelector:@selector(messengerChatWithDrawUpdateMsgId:success:)]) {
                    [self.delegate messengerChatWithDrawUpdateMsgId:@"" success:NO];
                }
            });
        }

    }};

    if (dispatch_get_specific(moduleQueueTag))
        block();
    else
        dispatch_async(moduleQueue, block);
}


/**
 *
 *  @brief  获取漫游消息记录的回调
 *
 *  @param iq
 *  @param basicTrackingInfo
 */
- (void)getRoamMessageRecordQueryIQ:(XMPPIQ *)iq withInfo:(XMPPBasicTrackingInfo *)basicTrackingInfo
{
    /*
     <iq type="result" id="URRYf-31" from="admin@m.com" to="xiongshihui@m.com/Spark 2.6.3"><req xmlns="com:yineng:offline "><resp>{"messageList":[{"sendTime":"","type":"","from":""}],"total":"150"}</resp><code>200</code></req></iq>
     */
    dispatch_block_t block = ^{ @autoreleasepool {

        //req
        NSXMLElement *req = [iq elementForName:@"req"];

        //响应码
        int code = [[req elementForName:@"code"] stringValueAsInt];

        //判断相应是否成功, 200为成功
        if (code == 200) {
            //解析出消息
            NSString *respStr = [[req elementForName:@"resp"] stringValue];
            if (![YNIMTool isEmptyString:respStr]) {
                NSArray*respArray=(NSArray*)[YNIMTool getJsonObjectFromJsonString:respStr];
                [YNContactManager parsingAndSaveRoamMessageWithInfo:respArray isMessageListData:NO isNewListPullData:NO];
            }
        }
        //在主线程发出通知提示漫游数据拉取结束
        dispatch_async(dispatch_get_main_queue(), ^{
            if ([self.delegate respondsToSelector:@selector(pullTheRoamingMessageFinished)]) {
                [self.delegate pullTheRoamingMessageFinished];
            }
        });
    }};

    if (dispatch_get_specific(moduleQueueTag))
        block();
    else
        dispatch_async(moduleQueue, block);
}

/**
 *
 *  @brief  消息列表获取漫游消息记录的回调
 *
 *  @param iq
 *  @param basicTrackingInfo
 */
- (void)getAitRoamMessageRecordQueryIQ:(XMPPIQ *)iq withInfo:(XMPPBasicTrackingInfo *)basicTrackingInfo
{
    /*
     <iq type="result" id="URRYf-31" from="admin@m.com" to="xiongshihui@m.com/Spark 2.6.3"><req xmlns="com:yineng:offline "><resp>{"messageList":[{"sendTime":"","type":"","from":""}],"total":"150"}</resp><code>200</code></req></iq>
     */
    dispatch_block_t block = ^{ @autoreleasepool {

        //req
        NSXMLElement *req = [iq elementForName:@"req"];

        //响应码
        int code = [[req elementForName:@"code"] stringValueAsInt];

        //判断相应是否成功, 200为成功
        if (code == 200) {
            //解析出消息
            NSString *respStr = [[req elementForName:@"resp"] stringValue];
            if (![YNIMTool isEmptyString:respStr]) {
                NSArray*respArray=(NSArray*)[YNIMTool getJsonObjectFromJsonString:respStr];
                [YNContactManager parsingAndSaveRoamMessageWithInfo:respArray isMessageListData:NO isNewListPullData:YES];
            }
        }

        //在主线程发出通知提示漫游数据拉取结束
        dispatch_async(dispatch_get_main_queue(), ^{
            if ([self.delegate respondsToSelector:@selector(updataRecentChatInterface)]) {
                [self.delegate updataRecentChatInterface];
            }
        });

    }};

    if (dispatch_get_specific(moduleQueueTag))
        block();
    else
        dispatch_async(moduleQueue, block);
}


/**
 *
 *  @brief  同步会话列表的回调
 *
 *  @param iq
 *  @param basicTrackingInfo
 */
- (void)handleSessionListQueryIQ:(XMPPIQ *)iq withInfo:(XMPPBasicTrackingInfo *)basicTrackingInfo
{
    /*
     <iq type="result" id="URRYf-31" from="admin@m.com" to="xiongshihui@m.com/Spark 2.6.3"><req xmlns="com:yineng:offline "><resp>{"messageList":[{"sendTime":"","type":"","from":""}],"total":"150"}</resp><code>200</code></req></iq>
     */
    dispatch_block_t block = ^{ @autoreleasepool {

        //req
        NSXMLElement *req = [iq elementForName:@"req"];

        //响应码
        int code = [[req elementForName:@"code"] stringValueAsInt];

        //判断相应是否成功, 200为成功
        if (code == 200) {
            NSString *respStr = [[req elementForName:@"resp"] stringValue];
            NSArray* respArray = (NSArray*)[YNIMTool getJsonObjectFromJsonString:respStr];

            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                //在这里，将数据保存在数据库里
                [YNContactManager saveSessionListWithInfo:respArray];
                debugLog(@"登录收取数据 第五步：更新消息列表消息状态");
                [YNMessageContactManager updateMessageSendingStateWhenLoggedIn];
                dispatch_async(dispatch_get_main_queue(), ^{
                    //是否为拉取同步消息列表
                    if ([self.delegate respondsToSelector:@selector(updataRecentChatInterface)]) {
                        [self.delegate updataRecentChatInterface];
                    }
                });
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    debugLog(@"登录收取数据结束 菊花消失");
                    if ([self.delegate respondsToSelector:@selector(loginLoadDataFinish)]) {
                        [self.delegate loginLoadDataFinish];
                    }
                });
                
            });

        }else{
            debugLog(@"登录时同步会话列表失败");
            debugLog(@"登录收取数据 第五步：更新消息列表消息状态");
            [YNMessageContactManager updateMessageSendingStateWhenLoggedIn];
            debugLog(@"登录收取数据结束 菊花消失");
            dispatch_async(dispatch_get_main_queue(), ^{
                if ([self.delegate respondsToSelector:@selector(loginLoadDataFinish)]) {
                    [self.delegate loginLoadDataFinish];
                }
            });
        }
    }};

    if (dispatch_get_specific(moduleQueueTag))
        block();
    else
        dispatch_async(moduleQueue, block);
}


//手动获取组织机构全量更新回调
- (void)handleManualFetchRosterQueryIQ:(XMPPIQ *)iq withInfo:(XMPPBasicTrackingInfo *)basicTrackingInfo
{

    dispatch_block_t block = ^{ @autoreleasepool {

        /** iq接收样式
         <iq type="result"
         id="URRYf-31"
         from="admin@m.com"
         to="xiongshihui@m.com/Spark 2.6.3">
         <req xmlns="com:yineng:orgget ">
         <resp>
         {"servertime":"","orgList":[{},{}],"userList":[{},{}],"relList ":[{},{}],"statusList":[{}]}
         </resp>
         <code>
         200
         </code>
         </req>
         </iq>
         **/
        if (iq)//如果iq不为空
        {
            NSXMLElement *req = [iq elementForName:@"req" xmlns:@"com:yineng:orgget"];

            //code
            int code = [[req elementForName:@"code"] stringValueAsInt];

            //200为成功返回数据, 其他数据不成功，不需要调用下面方法
            if (code == 200)
            {

                NSXMLElement *resp = [req elementForName:@"resp"];
                
                NSDictionary *dic = (NSDictionary*)[YNIMTool getJsonObjectFromJsonString:[resp stringValue]];
                
                if (dic!=nil)
                {
                    if (dic)
                    {
                        //清除所有用户，组织机构，用户组织机构关系
                        BOOL result = [YNContactManager deleteAllUserAndOrgTree];
                        if (result)
                        {
                            //保存组织机构，用户等信息
                            [YNContactManager saveContactOrgWithOrgDictionary:dic];
                        }
                    }
                }
            }
        }
        self.contactOrgDataIsSaveing = NO;
    }};

    if (dispatch_get_specific(moduleQueueTag))
        block();
    else
        dispatch_async(moduleQueue, block);

}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark XMPPStream Delegate - 收消息
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq
{
    debugLog(@"收到openfire的IQ数据：%@",iq);
    NSXMLElement *req = [iq elementForName:@"req"];
    if (req)
    {
        if([iq isResultIQ])
        {
            [xmppIDTracker invokeForElement:iq withObject:iq];

            NSXMLElement *notice = [iq elementForName:@"notice"];

            if (notice != nil)
            {
                debugLog(@"推送用户、组织机构、组织机构与用户关系变更通知");

                NSString *noticeXmlns = [notice xmlns];

                if ([noticeXmlns isEqualToString:@"com:yineng:notice"])
                {
                    NSString *type = [[notice elementForName:@"type"] stringValue];

                    if ([type isEqualToString:@"orgUpdate"])
                    {
                        debugLog(@"组织机构更新了啊");

                        [self manualFetchUserData];

                        return YES;
                    }
                }
            }

        }
        return YES;
    }

    return NO;
}

- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence
{
    debugLog(@"收到openfire的Presence数据：%@",presence);
}
- (void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error
{
    [self _setRequestedRoster:NO];
    [self _setHasRoster:NO];
}

#pragma mark-自定义方法
//获取初始化信息成功后，获取组织机构，群，讨论组等等信息.
- (void)fetchRosterData
{
    /**HX**
     这里执行的顺序调整为（保证前一个动作结束后才能执行下一个）：
     1 获取组织机构、用户等信息
     2 获取群组信息 获取讨论组信息
     3 获取用户状态信息
     4 同步会话列表
     5 更新消息列表消息状态
     end 菊花消失
     **/
    debugLog(@"登录收取数据 第一步：获取组织机构、用户等信息");
    [self fetchRoster];
}

//手动获取组织机构和群讨论组等信息
- (void)manualFetchUserData
{
    [self manualFetchRoster];
    [self fetchGroupInfo];
    [self fetchDiscussionGroupInfo];
}

@end


